﻿
namespace demo.Web.Services
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.ServiceModel.DomainServices.Hosting;
    using System.ServiceModel.DomainServices.Server;


    using Web.AzureTables;
    using Web.PresentationModel;

    [EnableClientAccess()]
    public class DeptDomainService : DomainService
    {


        #region Department get/add/update

        /// <summary>
        /// Demonstrates eager and lazy loading.
        /// For eager loading, Employees ARE included with departments
        /// For lazy loading, Employees ARE NOT included with departments
        /// </summary>
        /// <returns></returns>
        public IQueryable<PMDepartment> GetDepartmentsPM(bool eagerLoad)
        {
            DepartmentDataSource ds = new DepartmentDataSource(GetCompanyID());
            EmployeeDataSource eds = new EmployeeDataSource();


            // NOTE: Azure doesn't support linq projections, so we build an internal list as a work-around
            List<PMDepartment> depts = new List<PMDepartment>();

            var qry = ds.SelectAll();                   // find all the departments
            foreach (Department d in qry)
            {
                PMDepartment pmd = new PMDepartment();
                pmd.ID = d.MakeID();
                pmd.Name = d.name;

                // eager load Employees for this dept if requested
                if (eagerLoad == true)
                {
                    List<PMEmployee> emps = new List<PMEmployee>();
                    var empQry = eds.SelectAll(pmd.ID);
                    foreach (Employee e in empQry)
                    {
                        PMEmployee pme = new PMEmployee();
                        pme.ID = e.MakeID();
                        pme.Name = e.name;
                        pme.Addr = e.addr;
                        pme.Ext = e.ext;
                        pme.DeptID = pmd.ID;            // link back to parent
                        emps.Add(pme);
                    }
                    pmd.Employees = emps;
                }

                depts.Add(pmd);
            }

            return depts.AsQueryable(); ;
        }



        /// <summary>
        /// Add a new Department
        /// </summary>
        /// <param Name="pmDepartment"></param>
        public void InsertPMDepartment(PMDepartment pmDepartment)
        {
            DepartmentDataSource ds = new DepartmentDataSource(GetCompanyID());

            Department dbDept = new Department(GetCompanyID());
            dbDept.name = pmDepartment.Name;
            ds.Upsert(dbDept);
            pmDepartment.ID = dbDept.MakeID();          // return real Department ID to presentation model object
        }



        /// <summary>
        /// Update an enxisting Department (or add as new if no longer on file)
        /// </summary>
        /// <param Name="pmDept"></param>
        public void UpdatePMDepartment(PMDepartment pmDept)
        {
            DepartmentDataSource ds = new DepartmentDataSource(GetCompanyID());

            Department dbDept = new Department();
            try
            {
                dbDept = ds.LoadFromCompositeID(pmDept.ID);
            }
            catch
            {
                // dept no longer on file - treat as an 'add'
                dbDept = new Department(GetCompanyID());
            }

            dbDept.name = pmDept.Name;
            ds.Upsert(dbDept);

            pmDept.ID = dbDept.MakeID();            // just in case this was an 'add'
        }

        #endregion



        #region Employee get for dept/add new

        /// <summary>
        /// This is only used when lazy loading the departments.  When depts are eager loaded,
        /// the Employees are shipped to the client along with the dept rows.
        /// </summary>
        /// <param Name="DeptID"></param>
        /// <returns></returns>
        public IQueryable<PMEmployee> GetPMEmployeesForDept(string deptID)
        {
            EmployeeDataSource ds = new EmployeeDataSource();

            // NOTE: Azure doesn't support linq projections, so we build an internal list as a work-around
            List<PMEmployee> pmEmps = new List<PMEmployee>();

            var qry = ds.SelectAll(deptID);
            foreach (Employee ee in qry)
            {
                PMEmployee pme = new PMEmployee();
                pme.ID = ee.MakeID();
                pme.Name = ee.name;
                pme.Addr = ee.addr;
                pme.Ext = ee.ext;
                pme.DeptID = deptID;                    // link to parent

                pmEmps.Add(pme);

            }

            return pmEmps.AsQueryable();
        }


        /// <summary>
        /// add a new Employee
        /// </summary>
        /// <param Name="pmEmp"></param>
        public void InsertPMEmployee(PMEmployee pmEmp)
        {

            EmployeeDataSource ds = new EmployeeDataSource();

            Employee dbEmp = new Employee(pmEmp.DeptID);
            dbEmp.name = pmEmp.Name;
            dbEmp.addr = pmEmp.Addr;
            dbEmp.ext = pmEmp.Ext;

            ds.Upsert(dbEmp);
            
            pmEmp.ID = dbEmp.MakeID();          // return the real ID for this presentation model object

        }


        #endregion



        #region RIA hooks to step through the transaction flow

        /// <summary>
        /// called at the start of every RIA transaction
        /// </summary>
        /// <param Name="context"></param>
        public override void Initialize(DomainServiceContext context)
        {
            base.Initialize(context);
        }

        /// <summary>
        /// called at the start of a QUERY operation
        /// </summary>
        /// <param Name="queryDescription"></param>
        /// <param Name="validationErrors"></param>
        /// <param Name="totalCount"></param>
        /// <returns></returns>
        public override System.Collections.IEnumerable Query(QueryDescription queryDescription, out IEnumerable<ValidationResult> validationErrors, out int totalCount)
        {
            return base.Query(queryDescription, out validationErrors, out totalCount);
        }


        /// <summary>
        /// called with RIA INVOKE operation (i.e. an 'out of band' command or scalar operation)
        /// </summary>
        /// <param Name="invokeDescription"></param>
        /// <param Name="validationErrors"></param>
        /// <returns></returns>
        public override object Invoke(System.ServiceModel.DomainServices.Server.InvokeDescription invokeDescription, out System.Collections.Generic.IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> validationErrors)
        {
            return base.Invoke(invokeDescription, out validationErrors);
        } 

        /// <summary>
        /// called at the start of a SUBMIT operation to add/update/delete data
        /// order: submit; authorize; validate; execute; persist
        /// </summary>
        /// <param Name="changeSet"></param>
        /// <returns></returns>
        public override bool Submit(ChangeSet changeSet)
        {
            return base.Submit(changeSet);
        }

        protected override bool AuthorizeChangeSet()
        {
            return base.AuthorizeChangeSet();
        }

        protected override bool ValidateChangeSet()
        {
            return base.ValidateChangeSet();
        }

        protected override bool ExecuteChangeSet()
        {
            return base.ExecuteChangeSet();
        }

        protected override bool PersistChangeSet()
        {
            return base.PersistChangeSet();
        }


        /// <summary>
        /// called at the end of every RIA operation for final cleanup
        /// </summary>
        /// <param Name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        #endregion


        private string GetCompanyID()
        {
            return "ABC";                   // fake company ID to provide partition for depts.
        }

    }
}


